package com.dotndot.controller.utils;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.CDATASection;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class XMLUtils
{
    public static final String ID_NODE = "_NODE_";

    private Node m_oNode = null;
    private List<Map<String, Object>> m_oList = null;

    public XMLUtils( InputStream a_sXmlContent ) throws Exception
    {
        try
        {
            m_oNode = parseContent(a_sXmlContent);
        }
        catch( Exception e )
        {
            e.printStackTrace();
            throw new Exception("Data Parsing Error!");
        }
    }

    public static Document parseContent( InputStream input ) throws Exception
    {
        Document document = null;

        if( input == null )
            throw new Exception("Content is null!");

        DocumentBuilderFactory oFactory = DocumentBuilderFactory.newInstance();
        oFactory.setValidating(false);
        oFactory.setNamespaceAware(true);

        try
        {
            DocumentBuilder builder = oFactory.newDocumentBuilder();
            document = builder.parse(input);
            return document;
        }
        catch( Exception e )
        {
            throw e;
        }
    }

    public void setList( String a_sXpathExpression ) throws Exception
    {
        if( m_oList == null )
        {
            m_oList = new ArrayList<Map<String, Object>>();
        }
        else
        {
            m_oList.clear();
        }

        try
        {
            NodeList oNodeList = null;
            if( a_sXpathExpression.charAt(0) != '/' )
            {
                switch( m_oNode.getNodeType() )
                {
                    case Node.DOCUMENT_NODE:
                        Document document = (Document) m_oNode;
                        oNodeList = document.getElementsByTagName(a_sXpathExpression);
                        break;
                    case Node.ELEMENT_NODE:
                        Element element = (Element) m_oNode;
                        oNodeList = element.getElementsByTagName(a_sXpathExpression);
                        break;
                    default:
                        throw new Exception("Node-Type is invalid!");
                }
            }
            // else
            // {
            // oNodeList = XMLUtil.selectNodeList(m_oNode, a_sXpathExpression);
            // }

            Node oNode = null;
            Node oChildNode = null;
            NodeList oChildNodeList = null;
            Map<String, Object> oValue = null;
            int nNodeListLength = 0;
            if( oNodeList == null )
                nNodeListLength = 0;
            else
                nNodeListLength = oNodeList.getLength();
            int nLength = 0;
            String sLocalName = null;

            for( int i = 0; i < nNodeListLength; i++ )
            {
                oNode = oNodeList.item(i);
                oValue = new HashMap<String, Object>();

                oValue.put(ID_NODE, oNode);
                // ATTRIBUTE_NODE
                if( oNode.hasAttributes() )
                {
                    nLength = oNode.getAttributes().getLength();

                    for( int j = 0; j < nLength; j++ )
                    {
                        oChildNode = oNode.getAttributes().item(j);
                        sLocalName = oChildNode.getLocalName();
                        if( sLocalName != null )
                        {
                            oValue.put(sLocalName, getTextContent(oChildNode));
                        }
                    }
                }

                // ELEMENT_NODE
                oChildNodeList = oNode.getChildNodes();
                nLength = oChildNodeList.getLength();

                for( int j = 0; j < nLength; j++ )
                {
                    oChildNode = oChildNodeList.item(j);
                    sLocalName = oChildNode.getLocalName();
                    if( sLocalName != null )
                    {
                        oValue.put(sLocalName, getTextContent(oChildNode));
                    }
                }
                m_oList.add(oValue);
            }
        }
        catch( Exception e )
        {
            throw e;
        }
    }

    public int size()
    {
        return m_oList.size();
    }

    public String get( String a_sXpathExpression ) throws Exception
    {
        if( getNode(a_sXpathExpression) != null && getTextContent(getNode(a_sXpathExpression)) != null )
            return getTextContent(getNode(a_sXpathExpression));
        else
            return "";
    }

    public Node getNode( String a_sXpathExpression ) throws Exception
    {
        try
        {
            if( a_sXpathExpression.charAt(0) != '/' )
            {
                NodeList nodeList = null;
                switch( m_oNode.getNodeType() )
                {
                    case Node.DOCUMENT_NODE:
                        Document document = (Document) m_oNode;
                        nodeList = document.getElementsByTagName(a_sXpathExpression);
                        break;
                    case Node.ELEMENT_NODE:
                        Element element = (Element) m_oNode;
                        nodeList = element.getElementsByTagName(a_sXpathExpression);
                        break;
                    default:
                        throw new Exception("Node-Type is invalid!");
                }

                if( nodeList.getLength() > 0 )
                {
                    return nodeList.item(0);
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }
        catch( Exception e )
        {
            throw e;
        }
    }

    public static String getTextContent( Node a_oNode ) throws DOMException
    {

        String sTextContent = "";

        if( a_oNode == null )
        {
            return sTextContent;
        }
        if( a_oNode.getNodeType() == Node.ATTRIBUTE_NODE )
        {
            sTextContent = a_oNode.getNodeValue();
        }
        else
        {
            Node oChild = a_oNode.getFirstChild();

            if( oChild != null )
            {
                if( oChild.getNodeType() == Node.CDATA_SECTION_NODE )
                {
                    CDATASection cdata = (CDATASection) oChild;
                    sTextContent = cdata.getData();
                }
                else
                {
                    sTextContent = oChild.getNodeValue();
                }
            }
        }

        return sTextContent;
    }

    public String get( int a_nIndex, String a_sKey ) throws Exception
    {
        if( m_oList == null )
        {
            throw new Exception("List isn't Assigned!");
        }

        if( containsKey(a_nIndex, a_sKey) )
        {
            if( m_oList.get(a_nIndex) != null && m_oList.get(a_nIndex).get(a_sKey) != null )
                return m_oList.get(a_nIndex).get(a_sKey).toString();
            else
                return "";
        }
        else
        {
            return "";
        }
    }

    public boolean containsKey( int a_nIndex, String a_sKey )
    {
        if( m_oList.size() <= a_nIndex )
        {
            return false;
        }
        else
        {
            Map<String, Object> value = m_oList.get(a_nIndex);
            return value.containsKey(a_sKey);
        }
    }
}
